/**
 * 不要和老夫说什么工厂模式，工厂方法
 * 这里既没有内存分配，也没有虚基类
 * 
 * 注意:
 * mix_item 对于一些非基本类型(非 u32/i32/ixx/f32... 这些)，不保存副本，只是指向对应变量，
 * 所以 mix_item 对象的生命周期取决于被指向对象的生命周期
 * 
 * 一般是如下方式使用，将参数列表转换成 mix_item 数组，
 * 这样就完成的防止模板膨胀的第一步，然后将 items 数组传给一下级函数，
 * args 的生命周期长于 items 数组，所以 items 元素指向的参数可以在函数内部放心的使用
 * 
 * void foo(args_t && ... args) const {
 *    mix_item items[]        = { xforward(args)... };
 *    ...
 * }
 * 
 * ...
 * 
 * // 这种方式就不正确，指向的内容在 mix_item 构造后就析构了，
 * mix_item foo = 10;
 * // 可以理解为 10 存在栈上的一个临时的位置
 * // push 10               | 假设 10 存在栈 0x1000 这个位置
 * // foo point to 0x1000   | foo 构造，指向 0x1000 这个位置
 * // pop                   | foo 构造完成后，退栈，
 *                          | 0x1000 这块地址可能被其他函数调用的参数复用，所以此时 foo 会指向析构内容的地址
 */
#ifndef xpack_utils_mix_item
#define xpack_utils_mix_item
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::utils_mix_item::inc
#include"define/classify.hpp"
#include"define/nullref.hpp"
#include"interface/can_alloc.hpp"
#include"lang/cxx/private/cxx_helper.hpp"
#include"lang/cxx/private/format_base.hpp"
#include"lang/cxx/make_json_string.hpp"
#include"lang/cxx/strlize.hpp"
#include"lang/cxx.hpp"
#include"macro/xexport.hpp"
#include"meta/integer_type.hpp"
#include"meta/is_based_on.hpp"
#include"meta/is_float.hpp"
#include"meta/is_enum.hpp"
#include"meta/is_origin_array.hpp"
#include"meta/is_origin_arrayx.hpp"
#include"meta/is_origin_string.hpp"
#include"meta/is_ptr.hpp"
#include"meta/is_same.hpp"
#include"meta/is_xstruct.hpp"
#include"meta/more_fit.hpp"
#include"meta/origin_of.hpp"
#pragma pop_macro("xuser")

namespace mixc::utils_mix_item{
    template<class object_t, class item_t>
    concept can_mix_item_strlize = requires(object_t && value, inc::ialloc<item_t> const & alloc){
        inc::cxx<item_t>(value, alloc);
    };
}

namespace mixc::utils_mix_item::origin{
    template<class item_t>
    xstruct(
        xtmpl(mix_item, item_t),
        xwhos(inc::classify_t::utils_mix_item)
    )
    private:
        using cxx_t         = inc::cxx<item_t>;
        using lambda_t      = typename inc::cxx_helper<item_t>::__batches_lambda;
    public:
        constexpr mix_item(the_t const &) = default;
        constexpr mix_item(decltype(inc::nullref)) : mix_item(nullptr){}

        template<
            class obj_t, 
            class origin_t = inc::origin_of<obj_t, with_propcast>
        >
        requires(inc::is_mix_item<obj_t> == false)
        constexpr mix_item(obj_t && value) : 
            // this->is_in_secret() 不能放这里
            // inc::mix_item items[] = { args... }; // 初始化，
            // cxx.format_non_secret(items, alloc); // 在这里才决定将 l_is_secret = false，
            //                                      // 所以在 mix_item 构造时获取 is_in_secret() 是不正确的
            is_secret_type(inc::is_secret_type<origin_t>){
            this->initial((obj_t &&)value);
        }

    private:
        static bool is_in_secret(){
            return inc::cxx<item_t>::is_in_secret();
        }

        template<class obj_t>
        void initial(obj_t && value){
            // 当使用右值引用时，对于 obj_t 会保留引用标记
            using object_t              = inc::origin_of<obj_t>;

            // more_fit 回归
            // 需要支持类似 utils/counter 这样支持隐式转换成基本类型的结构体
            using result_t              = inc::more_fit<
                object_t, 
                bool, char, i08, i16, i32, i64, u08, u16, u32, u64, f32, f64
            >;

            using match_t               = typename result_t::type;

            // 允许以指针方式打印，因为实际情况可能刚好需要打印这个
            // if constexpr (inc::can_alloc<object_t, item_t>){
            //     return; // pass
            // }
            // else if constexpr (inc::can_stream<object_t, item_t>){
            //     return; // pass
            // }
            // else 
            if constexpr (inc::is_based_on<inc::format_base, object_t>){
                auto & target           = (voidp &)(this->value);
                this->fmt               = (& mix_item::template format<object_t>);
                target                  = (voidp)(& value);
            }
            else if constexpr (inc::is_origin_string<obj_t> or inc::is_cxx<obj_t> or inc::is_origin_arrayx<obj_t, item_t>){
                // 注意：
                // 宽字节需要注意长度问题
                auto   item             = (cxx_t(value));
                auto & target           = (item_t * &)(this->value);
                this->length            = (item->length);
                this->is_str            = (true);
                this->fmt               = (& mix_item::template strlize<const/*需要 const 修饰*/ item_t *>);
                target                  = (item_t *)(item);
            }
            else if constexpr (inc::is_enum<object_t>){
                using integer_enum      = inc::integer_type<object_t>;
                auto & target           = (integer_enum &)(this->value);
                this->fmt               = (& mix_item::template strlize<integer_enum>);
                target                  = (integer_enum)(object_t &)(value);
            }
            else if constexpr (inc::is_ptr<object_t>){
                auto & target           = (voidp &)(this->value);
                this->fmt               = (& mix_item::template strlize<voidp>);
                target                  = (voidp)(value);
            }
            // 先于 can_mix_item_strlize，
            // 原始数组会匹配 cxx_t(bool, alloc); 这个重载
            else if constexpr (inc::is_origin_array<object_t>){
                auto & target           = (object_t* &)(this->value);
                this->fmt               = (& mix_item::template strlize<object_t>);
                target                  = (object_t*)(&value);
            }
            else if constexpr (can_mix_item_strlize<object_t, item_t>){
                auto & target           = (object_t* &)(this->value);
                this->fmt               = (& mix_item::template strlizex<object_t>);
                target                  = (object_t*)(&value);
            }
            else if constexpr (result_t::index != not_exist){
                auto & target           = (match_t &)(this->value);
                this->fmt               = (& mix_item::template strlize<match_t>);
                target                  = (match_t)(value);
            }
            else if constexpr (inc::is_xstruct<object_t>){
                auto & target           = (voidp &)(this->value);
                this->fmt               = (& mix_item::template strlize<object_t>);
                target                  = (voidp)(& value);
            }
            else{
                auto & target           = (voidp &)(this->value);
                this->fmt               = (& mix_item::template strlize<voidp>);
                target                  = (voidp)(& value);
            }
        }

    public:
        void format(lambda_t const & alloc) const {
            this->fmt(this, alloc);
        }

        cxx_t to_cxx() const {
            return cxx_t{(item_t *)(this->value), this->length};
        }

        bool is_string() const {
            return this->is_str != 0;

            // 这里不用如下判断，因为存在空串误判的情况
            // return this->length != 0;
        }

    private:
        template<class object_t>
        static void strlize(mix_item const * self, lambda_t const & alloc){
            if (self->is_secret_type and the_t::is_in_secret()){
                cxx_t(::secret_mark, alloc);
            }
            else if constexpr (inc::is_float<object_t>){
                cxx_t(
                    (object_t &)(self->value), alloc
                );
            }

            // TODO：考虑其他类型的字符串
            else if constexpr (inc::is_same<asciis, object_t>){
                cxx_t(
                    cxx_t(
                        (object_t &)(self->value), self->length
                    ), alloc
                );
            }
            else if constexpr (inc::is_same<voidp, object_t>){
                item_t prefix[]     = { '0', 'x' };
                auto stream         = alloc;
                stream.output(prefix, 2);
                cxx_t((uxx)(self->value), inc::numeration_t::hex, stream);
            }
            // 原始数组会匹配 cxx_t(bool, alloc); 这个重载
            else if constexpr (not inc::is_xstruct<object_t> and not inc::is_origin_array<object_t>){
                cxx_t((object_t &)(self->value), alloc);
            }
            else if constexpr (inc::is_secret_type<object_t>){
                cxx_t(
                    the_t::is_in_secret() ? inc::make_json_string : inc::make_json_string_non_secret,
                    (typename object_t::origin_type)*(object_t *)(self->value), 
                    alloc
                );
            }
            else{
                cxx_t(
                    the_t::is_in_secret() ? inc::make_json_string : inc::make_json_string_non_secret,
                    *(object_t *)(self->value), alloc
                );
            }
        }

        // TODO:尽可能使用副本而非引用避免引用失效
        template<class object_t>
        static void strlizex(mix_item const * self, lambda_t const & alloc){
            auto & target = *(object_t *)self->value;
            cxx_t(target, alloc);
        }

        template<class object_t>
        static void format(mix_item const * self, lambda_t const & alloc){
            ((object_t *)self->value)->format(alloc);
        }

        using fmt_t             = void(*)(mix_item const * self, lambda_t const & alloc);

        mutable fmt_t   fmt                                 = nullptr;
        mutable uxx     length          : xbitsof(uxx) - 2  = 0;
        mutable uxx     is_str          : 1                 = 0;
        mutable uxx     is_secret_type  : 1                 = 0;
        mutable u64     value                               = 0;
    $
}

#endif

xexport_space(mixc::utils_mix_item::origin)
